use crate::errors::MyError;
use md5::Digest;
use md5::Md5;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use std::time::{SystemTime, UNIX_EPOCH};
use uuid::Uuid;

pub fn gen_uuid() -> String {
    let uid = Uuid::new_v4();
    uid.to_string()
}

pub fn is_not_empty_or<'a>(this: &'a str, or_val: &'a str) -> &'a str {
    if this.is_empty() {
        or_val
    } else {
        this
    }
}
pub fn to_ticks(time: &SystemTime) -> Result<u32, MyError> {
    let since_epoch = time.duration_since(UNIX_EPOCH)?;
    Ok(since_epoch.as_secs().try_into()?)
}
pub fn to_now_ticks() -> Result<u32, MyError> {
    to_ticks(&SystemTime::now())
}

pub fn to_md5(data: &str) -> String {
    let mut hasher = Md5::new();
    hasher.update(data);
    let result = hasher.finalize();
    format!("{:x}", result)
}

pub fn gen_random_num() -> Result<String, MyError> {
    let mut rng = rand::thread_rng();
    let current_time = to_ticks(&SystemTime::now())?;
    Ok(rng
        .gen_range(current_time..=current_time + 1000)
        .to_string())
}

const RANDOM_CODE_LEN: usize = 4;
type _RandomCode = [char; RANDOM_CODE_LEN];
pub struct RandomCode(pub _RandomCode);
pub fn gen_random_code() -> Result<RandomCode, MyError> {
    let result = thread_rng()
        .sample_iter(&Alphanumeric)
        .take(RANDOM_CODE_LEN)
        .map(char::from)
        .collect::<Vec<char>>();
    let mut code: _RandomCode = ['0'; RANDOM_CODE_LEN];

    for (index, n) in result.iter().enumerate() {
        code[index] = n.to_owned();
    }

    Ok(RandomCode(code))
}

impl From<RandomCode> for String {
    fn from(code: RandomCode) -> Self {
        format!("{}{}{}{}", code.0[0], code.0[1], code.0[2], code.0[3])
    }
}
